package com.why.zing.common.utils;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 18位和15位身份证编号验证解析工具类，提供身份证编号格式验证，获取性别，获取出生年月日信息功能
 *
 * @author why
 */
public class ResidentIDCardUtil {

    private ResidentIDCardUtil() {
    }

    /**
     * 代表出生的省市区县信息
     */
    private static final String ADDRESS_GROUP = "(?<Address>\\d{6})";

    private static final String BIRTHDAY_GROUP_NAME = "Birthday";
    /**
     * 出生年月日
     */
    private static final String BIRTHDAY_GROUP = String.format("(?<%s>(19|20)\\d{2}(0[1-9]|1[0-2])([0-2]\\d|3[01]))", BIRTHDAY_GROUP_NAME);

    private static final String SEQUENCE_GROUP_NAME = "Sequence";
    /**
     * 书序编号。奇数代表男性，偶数代表女性
     */
    private static final String SEQUENCE_GROUP = String.format("(?<%s>\\d{3})", SEQUENCE_GROUP_NAME);

    private static final String VERIFY_GROUP_NAME = "Verify";

    /**
     * 校验位
     */
    private static final String VERIFY_GROUP = String.format("(?<%s>[\\dXx])", VERIFY_GROUP_NAME);

    public static final String RESIDENT_ID_CARD_REGEX = String.format("^%s%s%s%s$", ADDRESS_GROUP, BIRTHDAY_GROUP, SEQUENCE_GROUP, VERIFY_GROUP);


    private static final String BIRTHDAY_GROUP_15 = String.format("(?<%s>\\d{2}(0[1-9]|1[0-2])([0-2]\\d|3[01]))", BIRTHDAY_GROUP_NAME);
    /**
     * 15位居民身份证编号规范
     */
    public static final String RESIDENT_ID_CARD_REGEX_15 = String.format("^%s%s%s$", ADDRESS_GROUP, BIRTHDAY_GROUP_15, SEQUENCE_GROUP);

    /**
     * 15位身份证编号正则表达式
     */
    public static final Pattern RESIDENT_ID_CARD_PATTERN_15 = Pattern.compile(RESIDENT_ID_CARD_REGEX_15);

    /**
     * 18位身份证编号正则表达式
     */
    public static final Pattern RESIDENT_ID_CARD_PATTERN = Pattern.compile(RESIDENT_ID_CARD_REGEX);

    /**
     * 17位加权数
     */
    private static final int[] SUM_WEIGHT = new int[]{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};

    /**
     * 11位校验位
     */
    private static final String[] VERRIFY_CODES = new String[]{"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"};

    /**
     * 验证居民身份证编号是否有效(支持15位和18位居民身份证编号)
     *
     * @param residentIDCard 身份证编号
     * @return true表示有效。false表示无效
     */
    public static boolean isValidateIDCard(final String residentIDCard) {
        if (null == residentIDCard) {
            return false;
        }

        Matcher mch15 = RESIDENT_ID_CARD_PATTERN_15.matcher(residentIDCard);
        if (mch15.find()) {
            return true;
        }

        Matcher mch = RESIDENT_ID_CARD_PATTERN.matcher(residentIDCard);
        if (mch.find() && verifyCard(residentIDCard)) {
            return true;
        }

        return false;
    }

    /**
     * 获取身份证中的出生年月日信息,如果获取不到则返回null
     *
     * @param residentIDCard 身份证编号
     */
    public static LocalDate getIdCardBirthday(final String residentIDCard) {
        if (Objects.isNull(residentIDCard)) {
            return null;
        }

        Matcher mch15 = RESIDENT_ID_CARD_PATTERN_15.matcher(residentIDCard);
        String birthday = null;

        if (mch15.find()) {
            birthday = mch15.group(BIRTHDAY_GROUP_NAME);
        } else {
            Matcher mch = RESIDENT_ID_CARD_PATTERN.matcher(residentIDCard);
            if (mch.find() && verifyCard(residentIDCard)) {
                birthday = mch.group(BIRTHDAY_GROUP_NAME);
            }
        }

        if (Objects.isNull(birthday)) {
            return null;
        }
        return LocalDate.parse(birthday, DateTimeFormatter.ofPattern("yyyyMMdd"));
    }

    /**
     * 验证身份证编号是否满足15位编码规范
     *
     * @param residentIDCard 15位身份证编号
     * @return true表示满足规范
     */
    public static boolean isValidateIdCard15(final String residentIDCard) {
        if (null == residentIDCard) {
            return false;
        }

        Matcher mch15 = RESIDENT_ID_CARD_PATTERN_15.matcher(residentIDCard);
        if (mch15.find()) {
            return true;
        }

        return false;
    }

    /**
     * 验证身份证编号是否满足15位编码规范
     *
     * @param residentIDCard 15位身份证编号
     * @return true表示满足规范
     */
    public static boolean isValidateIdCard18(final String residentIDCard) {
        if (null == residentIDCard) {
            return false;
        }

        Matcher mch = RESIDENT_ID_CARD_PATTERN.matcher(residentIDCard);
        if (mch.find() && verifyCard(residentIDCard)) {
            return true;
        }

        return false;
    }


    /**
     * 依据身份证编号获取性别信息(支持15位和18位居民身份证编号)
     *
     * @param residentIDCard 身份证编号
     * @return 0代表女性，1代表男性，-1代表身份证编码错误
     */
    public static int getSex(final String residentIDCard) {
        if (null == residentIDCard) {
            return -1;
        }

        int sex = -1;
        Matcher mch15 = RESIDENT_ID_CARD_PATTERN_15.matcher(residentIDCard);
        if (mch15.find()) {
            sex = Integer.parseInt(mch15.group(SEQUENCE_GROUP_NAME)) % 2;
            return sex;
        }

        Matcher mch = RESIDENT_ID_CARD_PATTERN.matcher(residentIDCard);
        if (mch.find() && verifyCard(residentIDCard)) {
            sex = Integer.parseInt(mch.group(SEQUENCE_GROUP_NAME)) % 2;
        }

        return sex;
    }

    /**
     * 获取出生日期信息，格式为yyyyMMdd
     *
     * @param residentIDCard 居民身份证编号
     * @return 返回null表示无效的身份证编号，否则返回有效的出生日期信息
     */
    public static String getBirthday(final String residentIDCard) {
        if (null == residentIDCard) {
            return null;
        }

        Matcher mch15 = RESIDENT_ID_CARD_PATTERN_15.matcher(residentIDCard);
        if (mch15.find()) {
            return String.format("19%s", mch15.group(BIRTHDAY_GROUP_NAME));
        }

        Matcher mch = RESIDENT_ID_CARD_PATTERN.matcher(residentIDCard);
        if (mch.find() && verifyCard(residentIDCard)) {
            return mch.group(BIRTHDAY_GROUP_NAME);
        }

        return null;
    }

    /**
     * 计算身份证校验和，
     * 身份证前17位数字与对应位数的加权数的乘机的和与11取模运算，并比较模运算结果与第18位校验位进行对比
     *
     * @param residentIDCard 身份证编号
     * @return true表示满足校验规则
     */
    private static boolean verifyCard(final String residentIDCard) {
        int sum = 0;
        int index = 0;
        for (; index < SUM_WEIGHT.length; index++) {
            sum += SUM_WEIGHT[index] * Integer.parseInt(residentIDCard.substring(index, index + 1));
        }

        int verifyIndex = sum % 11;
        String verifyCode = residentIDCard.substring(index, index + 1);

        return verifyCode.equalsIgnoreCase(VERRIFY_CODES[verifyIndex]);
    }
}
